import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
import trimesh
from time import time


def load_texture(texture_path):
    img = pygame.image.load(texture_path)
    # 将 Pygame Surface 对象转换为字符串格式的图像数据。
    img_data = pygame.image.tostring(img, "RGB", 1)     # 1 表示从顶部到底部的行顺序（通常是从上到下）。如果为 0，则表示从底部到顶部（通常是从下到上）。

    width, height = img.get_rect().size
    texture_id = glGenTextures(1)   # 为新的纹理对象分配一个唯一的标识符，这里指定生成 1 个纹理对象名称。如果需要生成多个纹理对象，可以传递一个大于 1 的值。
    glBindTexture(GL_TEXTURE_2D, texture_id)    # 绑定一个指定的纹理对象，以便在后续的纹理操作中引用。GL_TEXTURE_2D:指定操作的是一个二维纹理

    # 为当前绑定的纹理对象设置参数。
    # GL_TEXTURE_2D：目标纹理类型。(常见的还有GL_TEXTURE_1D，GL_TEXTURE_3D)
    # GL_TEXTURE_MIN_FILTER：要设置的纹理参数名。指定纹理缩小过滤的参数，即当纹理图像被缩小时应该如何处理。
    # GL_TEXTURE_MAG_FILTER：要设置的纹理参数名。指定纹理放大过滤的参数，即当纹理图像被放大时应该如何处理。
    # GL_LINEAR：线性过滤模式。当纹理被缩小时，使用线性插值的方法来计算纹素（texel）值。
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)    # 设置当纹理缩小时使用线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)    # 设置当纹理放大时使用线性插值

    # 功能：指定二维纹理图像的详细信息和数据。 作用：将图像数据上传到当前绑定的二维纹理对象中。
    # 参数详解：1）GL_TEXTURE_2D：目标纹理对象  2）0：指定基本图像级别。0 表示最基础的完整图像。 
    # 3）GL_RGB：指定纹理对象的内部格式。GL_RGB 表示每个纹理元素包含红、绿、蓝三个颜色分量。
    # 4，5）width, height：指定纹理图像的尺寸（宽度和高度）
    # 6）0：边框宽度。必须为 0。OpenGL 不再支持非零的边框宽度。
    # 7）GL_RGB：同3）
    # 8）GL_UNSIGNED_BYTE：指定图像数据的每个颜色分量的类型。GL_UNSIGNED_BYTE 表示每个颜色分量是一个无符号字节（8 位）。
    # 9）img_data：指向图像数据的指针。包含实际图像数据的数组或指针。在此例中，img_data 是一个包含 RGB 数据的字符串或字节数组。
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)

    return texture_id


def draw_mesh(vertices, faces, uv_coords, texture_id):
    # glEnable 是一个 OpenGL 函数，用于启用某种特定的 OpenGL 功能或特性。
    # 二维纹理（GL_TEXTURE_2D）是一种将二维图像映射到三维模型表面的技术。它通过将图像的像素颜色值应用到模型的各个顶点，从而使模型表面看起来更加真实和细致。
    glEnable(GL_TEXTURE_2D)

    # glBindTexture 是一个 OpenGL 函数，用于绑定一个纹理对象。
    glBindTexture(GL_TEXTURE_2D, texture_id)    # 绑定一个纹理对象，以便在后续的纹理操作中引用该纹理。


    # glBegin 是一个 OpenGL 函数，用于开始定义一系列顶点，这些顶点将构成某种几何图元。  与 glEnd 对应
    glBegin(GL_TRIANGLES)   # GL_TRIANGLES 是一个枚举值，表示几何图元的类型是三角形。这一句的作用是开始定义一个几何图元（在本例中是三角形）

    for face in faces:
        for vertex_idx in face:
            glTexCoord2fv(uv_coords[vertex_idx])    # 为当前顶点设置纹理坐标，用于纹理映射。
            glVertex3fv(vertices[vertex_idx])   # 为当前顶点设置三维坐标，用于指定几何图形的顶点位置。
    glEnd()     # glEnd() 的作用是结束一个由 glBegin 开始的顶点定义序列

    # glDisable(GL_TEXTURE_2D) 的作用是禁用 OpenGL 的二维纹理功能。这意味着在后续的绘制操作中，不会应用任何绑定的二维纹理。这通常用于确保在不需要纹理时避免对渲染过程的影响。
    glDisable(GL_TEXTURE_2D)    # 禁用二维纹理功能



def main():
    smpl_obj_path = 'obj_models/thuman2/scans/0525/0525.obj'
    texture_path = 'obj_models/thuman2/scans/0525/material0.jpeg'

    # 加载网格和纹理
    mesh = trimesh.load(smpl_obj_path, process=False)
    vertices = mesh.vertices
    faces = mesh.faces
    uv_coords = mesh.visual.uv

    # 初始化 Pygame 和 OpenGL
    pygame.init()   # 初始化所有pygame模块
    display = (1300, 800)   # 设置窗口大小（宽高），单位像素
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)    # 创建一个pygame的窗口，并设置显示模式。DOUBLEBUF：启用双缓冲，减少屏幕闪烁。OPENGL：启用 OpenGL 渲染上下文。
    glEnable(GL_DEPTH_TEST)     # 启用深度测试。确保OpenGL在绘制图像时进行深度测试，以正确处理前后物体的遮挡关系。
    gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)    # 设置透视投影矩阵
    # 45：视角的垂直视场角度（以度为单位），这里为 45 度。  (display[0] / display[1])：窗口的宽高比（aspect ratio）。
    # 0.1：近裁剪平面（near clipping plane）的距离。    50.0：远裁剪平面（far clipping plane）的距离。（单位取决于模型单位）

    glTranslatef(0.0, 0.0, -1.5)    # xyz轴上的平移变换：正数表示沿某个轴方向，负数表示反方向。z轴上正表示靠近视点(屏幕)位置，负表示原理视点

    # 打印 GPU 信息
    print("Renderer:", glGetString(GL_RENDERER).decode())
    print("OpenGL version supported:", glGetString(GL_VERSION).decode())

    texture_id = load_texture(texture_path) 


    clock = pygame.time.Clock()
    while True:
        t1 = time()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return
        # 清除特定的缓冲区。
        # GL_COLOR_BUFFER_BIT：颜色缓冲区标志。指定清除颜色缓冲区。颜色缓冲区存储了每个像素的颜色值。
        # GL_DEPTH_BUFFER_BIT：深度缓冲区标志。 指定清除深度缓冲区。深度缓冲区存储了每个像素的深度信息（距离观察者的距离）。
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 清除 颜色缓冲区 和 深度缓冲区

        # 应用旋转变换。
        # 参数含义：1：旋转角度     3,1,1：指定旋转轴的方向向量，即旋转轴的 x、y 和 z 分量
        glRotatef(1, 3, 1, 1)   # 绕方向向量绕(3, 1, 1) 轴旋转 1 度

        # 在屏幕上绘制一个带有纹理的三维网格。
        draw_mesh(vertices, faces, uv_coords, texture_id)

        pygame.display.flip()   # 更新整个显示窗口，将所有在内存缓冲区中进行的绘制操作显示到屏幕上。
        print(f"耗时: {(time()-t1):2f} s")

        clock.tick(60)  # # 将帧率限制为每秒 30 帧




if __name__ == "__main__":
    main()
